 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.update.ui;

 import java.util.ArrayList ;
 import java.util.HashMap ;

 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.MultiStatus;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.update.core.IFeature;
 import org.eclipse.update.core.ISite;
 import org.eclipse.update.core.ISiteWithMirrors;
 import org.eclipse.update.core.IURLEntry;
 import org.eclipse.update.core.model.InstallAbortedException;
 import org.eclipse.update.internal.core.Messages;
 import org.eclipse.update.internal.core.UpdateCore;
 import org.eclipse.update.internal.operations.UpdateUtils;
 import org.eclipse.update.internal.ui.*;
 import org.eclipse.update.internal.ui.wizards.*;
 import org.eclipse.update.operations.IInstallFeatureOperation;
 import org.eclipse.update.operations.OperationsManager;
 import org.eclipse.update.search.IUpdateSearchResultCollector;
 import org.eclipse.update.search.IUpdateSearchResultCollectorFromMirror;
 import org.eclipse.update.search.UpdateSearchRequest;

 /**
  * An UpdateJob performs the lookup for new features or updates to the existing
  * features, depending on how you construct it.
  *
  * @since 3.1
  */
 public class UpdateJob extends Job {

     private class SearchResultCollector implements IUpdateSearchResultCollector {
         public void accept(IFeature feature) {
             IInstallFeatureOperation operation = OperationsManager
                     .getOperationFactory().createInstallOperation(null,
                             feature, null, null, null);
             updates.add(operation);
         }
     }
     
     /**
      * The job family to use for queries
      */
     public static final Object FAMILY = new Object ();

     /**
      * The job family to use for queries
      *
      * @deprecated use FAMILY
      */
     public static final Object family = FAMILY;

     private IUpdateSearchResultCollector resultCollector;

     private UpdateSearchRequest searchRequest;

     private ArrayList updates;

     private boolean isUpdate;

     private boolean download;

     private boolean isAutomatic;

     private IStatus jobStatus = Status.OK_STATUS;
     
     private int mirrorIndex;

     /**
      * Use this constructor to search for updates to installed features
      *
      * @param isAutomatic
      * true if automatically searching for updates
      * @param name
      * the job name
      * @param download
      * download updates automatically
      */
     public UpdateJob(String name, boolean isAutomatic, boolean download) {
         this(name, isAutomatic, download, null);
     }

     /**
      * Use this constructor to search for updates to installed features
      *
      * @param isAutomatic
      * true if automatically searching for updates
      * @param name
      * the job name
      * @param download
      * download updates automatically
      * @param features
      * the features to search for updates. If you want to search all
      * features, pass a null array or use the other constructor.
      */
     public UpdateJob(String name, boolean isAutomatic, boolean download,
             IFeature[] features) {
         super(name);
         this.isUpdate = true;
         this.isAutomatic = isAutomatic;
         this.download = download;
         updates = new ArrayList ();
         searchRequest = UpdateUtils.createNewUpdatesRequest(features, isAutomatic);
         setPriority(Job.DECORATE);
         mirrorIndex = 0;
     }

     /**
      * Use this constructor to search for features as indicated by the search
      * request
      *
      * @param name
      * the job name
      * @param searchRequest
      * the search request to execute
      */
     public UpdateJob(String name, UpdateSearchRequest searchRequest) {
         super(name);
         this.searchRequest = searchRequest;
         updates = new ArrayList ();
         setPriority(Job.DECORATE);
     }

     /**
      * Returns true if the job is performing update lookups, or false when
      * searching for new features
      *
      * @return true when searching for updates of existing features
      */
     public boolean isUpdate() {
         return isUpdate;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.core.internal.jobs.InternalJob#belongsTo(java.lang.Object)
      */
     public boolean belongsTo(Object family) {
         return UpdateJob.FAMILY == family;
     }

     /**
      * Runs the job and returns the OK status. Call getStatus() to get the
      * actual execution status.
      *
      * @param monitor
      * progress monitor
      * @return IStatus the return code is always OK
      */
     public IStatus run(IProgressMonitor monitor) {
         if (isUpdate)
             jobStatus = runUpdates(monitor);
         else
             jobStatus = runSearchForNew(monitor);
         return Status.OK_STATUS;
     }

     private IStatus runSearchForNew(IProgressMonitor monitor) {
         if (UpdateCore.DEBUG) {
             UpdateCore.debug("Search for features started."); //$NON-NLS-1$
 }

         try {
             if (resultCollector == null)
                 resultCollector = new ResultCollectorWithMirrors();
             searchRequest.performSearch(resultCollector, monitor);
             if (UpdateCore.DEBUG) {
                 UpdateCore.debug("Automatic update search finished - " //$NON-NLS-1$
 + updates.size() + " results."); //$NON-NLS-1$
 }
             return Status.OK_STATUS;
         } catch (CoreException e) {
             return e.getStatus();
         } catch (OperationCanceledException ce) {
             return Status.CANCEL_STATUS;
         }
     }

     private IStatus runUpdates(IProgressMonitor monitor) {
         ArrayList statusList = new ArrayList ();
         if (UpdateCore.DEBUG) {
             if (isAutomatic)
                 UpdateCore.debug("Automatic update search started."); //$NON-NLS-1$
 else
                 UpdateCore.debug("Update search started."); //$NON-NLS-1$
 }

         if (resultCollector == null)
             resultCollector = new ResultCollectorWithMirrors();
         try {
             searchRequest.performSearch(resultCollector, monitor);
         } catch (CoreException e) {
             statusList.add(e.getStatus());
         } catch (OperationCanceledException ce) {
             return Status.CANCEL_STATUS;
         }
         if (UpdateCore.DEBUG) {
             UpdateCore.debug("Automatic update search finished - " //$NON-NLS-1$
 + updates.size() + " results."); //$NON-NLS-1$
 }
         if (updates.size() > 0) {
             // silently download if download enabled
 if (download) {
                 if (UpdateCore.DEBUG) {
                     UpdateCore.debug("Automatic download of updates started."); //$NON-NLS-1$
 }
                 for (int i = 0; i < updates.size(); i++) {
                     IInstallFeatureOperation op = (IInstallFeatureOperation) updates
                             .get(i);
                     IFeature feature = op.getFeature();
                     try {
                         UpdateUtils.downloadFeatureContent(op.getTargetSite(),
                                 feature, null, monitor);
                     } catch (InstallAbortedException e) {
                         return Status.CANCEL_STATUS;
                     } catch (CoreException e) {
                         statusList.add(e.getStatus());
                         updates.remove(i);
                         i -= 1;
                     }
                 }
                 if (UpdateCore.DEBUG) {
                     UpdateCore.debug("Automatic download of updates finished."); //$NON-NLS-1$
 }
             }
         }

         if (statusList.size() == 0)
             return Status.OK_STATUS;
         else if (statusList.size() == 1)
             return (IStatus) statusList.get(0);
         else {
             IStatus[] children = (IStatus[]) statusList
                     .toArray(new IStatus[statusList.size()]);
             return new MultiStatus("org.eclipse.update.ui", //$NON-NLS-1$
 ISite.SITE_ACCESS_EXCEPTION, children,
                     Messages.Search_networkProblems,
                     null);
         }
     }

     /**
      * Returns an array of features to install
      *
      * @return IInstallFeatureOperation[]
      */
     public IInstallFeatureOperation[] getUpdates() {
         return (IInstallFeatureOperation[]) updates
                 .toArray(new IInstallFeatureOperation[updates.size()]);
     }

     /**
      * Returns the job status upon termination.
      *
      * @return IStatus
      */
     public IStatus getStatus() {
         return jobStatus;
     }

     /**
      * Returns the update search request for this job.
      *
      * @return UpdateSearchRequest
      */
     public UpdateSearchRequest getSearchRequest() {
         return searchRequest;
     }

     private class ResultCollectorWithMirrors extends SearchResultCollector
             implements IUpdateSearchResultCollectorFromMirror {

         private HashMap mirrors = new HashMap (0);

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.update.search.IUpdateSearchResultCollectorFromMirror#getMirror(org.eclipse.update.core.ISite,
          * java.lang.String)
          */
         public IURLEntry getMirror(final ISiteWithMirrors site,
                 final String siteName) throws OperationCanceledException {
             if (isUpdate && isAutomatic)
                 return null;
             if (mirrors.containsKey(site))
                 return (IURLEntry) mirrors.get(site);
             try {
                 boolean automaticallyChooseMirrors = UpdateCore.getPlugin().getPluginPreferences().getBoolean(UpdateCore.P_AUTOMATICALLY_CHOOSE_MIRROR);
                 
                 IURLEntry[] mirrorURLs = site.getMirrorSiteEntries();
                 if (mirrorURLs.length == 0)
                     return null;
                 else if (automaticallyChooseMirrors){
                     return mirrorURLs[mirrorIndex];
                 } else {
                     // here we need to prompt the user
 final IURLEntry[] returnValue = new IURLEntry[1];
                     final boolean [] canceled = new boolean[1];
                     UpdateUI.getStandardDisplay().syncExec(new Runnable () {
                         public void run() {
                             MirrorsDialog dialog = new MirrorsDialog(UpdateUI
                                     .getActiveWorkbenchShell(), site, siteName);
                             dialog.create();
                             int result = dialog.open();
                             if (result==MirrorsDialog.CANCEL) {
                                 canceled[0] = true;
                             }
                             IURLEntry mirror = dialog.getMirror();
                             mirrors.put(site, mirror);
                             returnValue[0] = mirror;
                         }
                     });
                     if (canceled[0])
                         throw new OperationCanceledException();
                     return returnValue[0];
                 }
             } catch (CoreException e) {
                 return null;
             }
         }
     }
 }

